#include "Scene.h"
#include <stdexcept>

namespace SEngine
{
    Actor & Scene::CreateActor()
    {
        this->m_actors.emplace_back(std::make_shared<Actor>(*this));
        Actor & actor = *this->m_actors.back();
        actor.AddComponentSignal.AddSlot([this](IComponent & comp) { this->AddComponentSlot(comp); return SlotState::Keep; });
        actor.RemoveComponentSignal.AddSlot([this](IComponent & comp) { this->RemoveComponentSlot(comp); return SlotState::Keep; });
        return actor;
    }

    void Scene::DestroyActor(Actor &actor)
    {
        actor.ForeachChildren([this](Actor & child) {
            this->DestroyActor(child);
        });
        actor.ClearComponents();
        m_actors.erase(std::remove_if(
            m_actors.begin(),
            m_actors.end(),
            [&](std::shared_ptr<Actor> & elem) {
                return elem.get() == &actor;
            }
        ), m_actors.end());
    }

    void Scene::OnBeforeUpdate()
    {
        for (auto & actor : m_actors) {
            actor->BeforeUpdate();
        }
    }

    void Scene::OnUpdate(int delta)
    {
        for (auto & actor : m_actors) {
            actor->OnUpdate(delta);
        }
    }
    
    void Scene::OnAfterUpdate()
    {
        for (auto & actor : m_actors) {
            actor->AfterUpdate();
        }
    }

    void Scene::ForeachActor(std::function<void(Actor &)> &&fn)
    {
        for (auto & actor : m_actors) {
            fn(*actor);
        }
    }
    
    void Scene::AddComponentSlot(IComponent & comp)
    {
        m_componts[comp.Hash()].emplace_back(&comp);
        this->AddComponentSignal.Invoke(comp);
    }
    
    void Scene::RemoveComponentSlot(IComponent & comp)
    {
        this->RemoveComponentSignal.Invoke(comp);
        auto & comps = m_componts[comp.Hash()];
        comps.erase(std::remove_if(comps.begin(), comps.end(), [&] (IComponent * elem) { return &comp == elem; }), comps.end());
    }
} // namespace SEngine
